#!/usr/bin/env bash
# $LastChangedDate: 2011-12-14 20:39:41 -0500 (Wed, 14 Dec 2011) $
# LEGAL: COPYRIGHT (C) 2010 JIM E. BROOKS WWW.PALOMINO3D.ORG
# Do some checks before compiling.
# cmake captures output to stderr and only prints messages
# from this script if it returns a non-zero exit code.
#-------------------------------------------------------------------------------

#-------------------------------------------------------------------------------
# Configuration:
LUA_DIRS="scripts tools"
#-------------------------------------------------------------------------------

TOP_SRC_DIR=$1
if [ "$TOP_SRC_DIR" = "" ]; then
    TOP_SRC_DIR=src
fi
[ -d $TOP_SRC_DIR ] || exit 1

# lwlint available?
if ( type -pf lwlint >/dev/null 2>&1 ); then
    echo "checks.sh: will use lwlint."
    HAVE_LWLINT=1
fi

GREP="grep -H -n"

Checks()
{
    SRC_DIR=$1

    # Run lwlint if available.
    if [ $HAVE_LWLINT ]; then
        if ( ls ${SRC_DIR}/*.cc >/dev/null 2>&1 ); then
            if ( ! lwlint ${SRC_DIR}/*.cc ${SRC_DIR}/*.hh ); then
                echo "ERROR: lwlint found errors." >/dev/stderr
                exit 1
            fi
        fi
    fi

    #-----------------------------------------------
    # Serious C++ pitfall are virtual const methods.
    # Find prototypes of virtual const methods:
    if ( $GREP "virtual.*const;\\|virtual.*const {\\|virtual.*const^\\|virtual.*const = 0;" $SRC_DIR/*.* ); then
        echo "ERROR: virtual const methods open a C++ pitfall!" >/dev/stderr
        exit 1
    fi
    # (Checking all const methods is too pedantic.)
    # (Some const methods are useful in basic classes.)
    # (Checking prototypes of virtual const methods should be sufficient.)
    # Find definitions of const method (no way to distinguish between virtual or non-virtual):
    #if ( $GREP ".*[)] const^" $SRC_DIR/*.* ); then
    #    echo "ERROR: virtual const methods open a C++ pitfall!" >/dev/stderr
    #    exit 1
    #fi
    # Consequently, go ahead and find prototypes of non-virtual const methods.
    #if ( $GREP ".*[)] const;\\|.*[)] const {" $SRC_DIR/*.* ); then
    #    echo "ERROR: Avoid const methods because of pitfall in virtual const methods." >/dev/stderr
    #    exit 1
    #fi
    #-----------------------------------------------

    # "namespace global" singular is correct (minor).
    if ( $GREP -i "namespace globals" $SRC_DIR/*.* ); then
        echo "ERROR: namespace globals should be singular (minor)" >/dev/stderr
        exit 1
    fi

    # Catch: iter < end()
    if ( $GREP -i "iter.* < .*end[(]" $SRC_DIR/*.* ); then
        echo "ERROR: iter < end() is wrong!" >/dev/stderr
        exit 1
    fi

    # A habitual mistake that causes a buffer overrun is passing sizeof()
    # to InitArray() which expects an element count, not a byte count.
    if ( $GREP "InitArray.*sizeof" $SRC_DIR/*.* ); then  # not grep -q
        echo "ERROR: sizeof() cannot be passed to InitArray()" >/dev/stderr
        exit 1
    fi

    # Catch writing #if DEBUG2.  #if DEBUG == 2 is correct.
    if ( $GREP "if.*DEBUG2" $SRC_DIR/*.* ); then
        echo "ERROR: should be DEBUG==2" >/dev/stderr
        exit 1
    fi

    # Catch this mistake THREAD_CODE( Atomic::Add() )
    # because when compiled single-thread it expands nothing.
    if ( $GREP "\(^\|[^/]\)THREAD_CODE.*Atomic" $SRC_DIR/*.* ); then
        echo "ERROR: THREAD_CODE( Atomic ) expands nothing if ! COMPILE_THREADS" >/dev/stderr
        exit 1
    fi

    # Should be COMPILE_THREADS (plural).
	# Catch COMPILE_THREAD[EOL] / COMPILE_THREAD[SPC] / COMPILE_THREADK[junk char]
	# Allow COMPILE_THREADS_POSIX etc
    if ( $GREP "if COMPILE_THREAD$\\|if COMPILE_THREAD \\|if COMPILE_THREADS[^ _]" $SRC_DIR/*.* ); then
        echo "ERROR: Should be COMPILE_THREADS (plural)." >/dev/stderr
        exit 1
    fi

    # shptr shouldn't be a reference.
    FILES=
    for FILE in $SRC_DIR/*.*
    do
        if ( echo $FILE | $GREP -q -v shptr ); then
            FILES="$FILES $FILE"
        fi

        # Allow: Class<shptr<Class> >&
        if ( echo $FILE | $GREP -q -v shptr ) && ( $GREP "[^<]shptr<.*>&" $FILES ); then
            echo "ERROR: shptr shouldn't be a reference" >/dev/stderr
            exit 1
        fi

        if ( echo $FILE | $GREP -q -v shptr ) && ( $GREP "[^<]RefPtr<.*>&" $FILES ); then
            echo "ERROR: RefPtr shouldn't be a reference" >/dev/stderr
            exit 1
        fi

    done

    # Catch using C headers instead of compatible superset C++ headers.
    # Can causes mysterious compile errors eg std::abs() will be missing variants.
    if ( $GREP "include..stdlib.h\\|include..stdio.h\\|include..math.h\\|include..string.h\\|include..errno.h" $SRC_DIR/*.* ); then
        echo "ERROR: should use C++ headers instead of C headers" >/dev/stderr
        exit 1
    fi

    # Should write Count instead.
    if ( $GREP "Cnt\\|CNT\\| cnt" $SRC_DIR/*.* ); then
        echo "WARNING: Write Count instead." >/dev/stderr
        #exit 1
    fi

	# C preprocessor won't even complain.
    if ( $GREP "[#][ ]*elseif" $SRC_DIR/*.* ); then
        echo "ERROR: #elseif should be #elif" >/dev/stderr
        exit 1
    fi

    # For coding style, check if src file has tab chars.
    # Finding tab chars with grap is almost impossible.
    for FILE in $SRC_DIR/*.*
    do
        TABS=`perl -n -e '/\011/ and print;' ${FILE}`
        if [ "$TABS" != "" ]; then
           echo "WARNING: ${FILE} has tab chars."
        fi
    done
}

# For each subdir of SRC_DIR.
for SUBDIR in $TOP_SRC_DIR/*;
do
    if [ -d $SUBDIR ]; then
        if ( echo $SUBDIR | $GREP -q "deprecated\|lua_lang" ); then
            :
        else
            Checks $SUBDIR
        fi
    fi
done

# Check Lua scripts.
for LUA_DIR in $LUA_DIRS
do
    if [ ! -d $LUA_DIR ]; then
        echo "ERROR: LUA_DIR ${LUA_DIR} doesn't exist" >/dev/stderr
        exit 1
    fi

    # Find Lua scripts in this directory.
    LUA_SCRIPTS="`find ${LUA_DIR} -maxdepth 1 \( -name '*.lua' -or -name '*.py.lua' \)`"

    # Check Lua scripts using lint.lua.
    if [ "$LUA_SCRIPTS" != "" ]; then  # without args lint.lua would print help
        #echo "Checking for lint in ${LUA_SCRIPTS}" >/dev/stderr
        lua tools/lint.lua $LUA_SCRIPTS >/dev/stderr
        RC=$?
        if [ "$RC" = "1" ]; then
            exit 1
        elif [ "$RC" = "127" ]; then
            echo "Lua isn't installed, skipping lint checks (ok)." >/dev/stderr
            break  # no point in continuing
        fi
    fi
done

# Check common typos in src/ and doc/
if ( $GREP -r -i "frustrum\|fustrum" src doc | grep -v svn ); then
    echo "WARNING: correct spelling is 'frustum'." >/dev/stderr
fi

echo "Checks passed (ok)."
exit 0
